package demo

import (
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"io/ioutil"
	"net/http"
	"os"
	"reflect"
	"strconv"
	"test_singo/models"
	"time"
)

// 参考文档
// https://www.cnblogs.com/chaoyangxu/p/12156385.html
// 有道云笔记 gin 文档

func Demo(ctx *gin.Context) {
	ctx.JSON(200, gin.H{
		"msg": "this is a demo",
	})
}

// api参数
func ApiParams(ctx *gin.Context) {
	name := ctx.Param("name")
	action := ctx.Param("action")
	ctx.String(http.StatusOK, name+" is "+action)
}

// url参数
// /v1/user&name=donghuan
func UrlParams(ctx *gin.Context) {
	name := ctx.DefaultQuery("name", "donghuan")
	ctx.String(http.StatusOK, name)
}

// form表单提交
func FormPost(ctx *gin.Context) {
	name := ctx.DefaultPostForm("username", "")
	pass := ctx.DefaultPostForm("password", "")
	hobby := ctx.PostFormArray("hobby")
	ctx.JSON(http.StatusOK, gin.H{
		"name":  name,
		"pass":  pass,
		"hobby": hobby,
	})
}

// 基本的post请求, 见下面绑定请求参数案例
func BasicPost(ctx *gin.Context) {
	name := ctx.PostForm("username")
	ctx.JSON(http.StatusOK, gin.H{
		"username": name,
	})
}

// 上传单个文件
func SingleFileUpload(ctx *gin.Context) {
	file, _ := ctx.FormFile("file")
	// 默认保存到根目录
	timeUnix := strconv.FormatInt(time.Now().Unix(), 10)   // 获取时间戳并转成字符串
	filePath := "upload/" + timeUnix + "-" + file.Filename // 设置保存文件的路径，不要忘了后面的文件名
	// 有个坑，upload需要手动创建，没有不会报错, 只是不会保存
	ctx.SaveUploadedFile(file, filePath)
	ctx.JSON(200, gin.H{
		"msg": file.Filename,
	})
}

// 上传多个文件
func MultiFileUpload(ctx *gin.Context) {
	form, err := ctx.MultipartForm()
	if err != nil {
		ctx.JSON(500, gin.H{
			"msg": "文件上传失败！",
		})
	}

	files := form.File["file"]
	for _, file := range files {
		// 默认保存到根目录
		timeUnix := strconv.FormatInt(time.Now().Unix(), 10)   // 获取时间戳并转成字符串
		filePath := "upload/" + timeUnix + "-" + file.Filename // 设置保存文件的路径，不要忘了后面的文件名
		// 有个坑，upload需要手动创建，没有不会报错, 只是不会保存
		ctx.SaveUploadedFile(file, filePath)
	}

	ctx.JSON(200, gin.H{
		"msg": "文件上传成功！",
	})
}

// 绑定参数 - 绑定请求参数(body)
func BindRequestBody(ctx *gin.Context) {
	var body RequestLogin
	if err := ctx.ShouldBindJSON(&body); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	if body.Username != "root" || body.Password != "123" {
		ctx.JSON(http.StatusBadRequest, gin.H{"status": "304"})
		return
	}
	ctx.JSON(http.StatusOK, gin.H{"status": "200"})
}

// 绑定参数 - 绑定form
func BindForm(ctx *gin.Context) {
	var body RequestForm
	if err := ctx.ShouldBind(&body); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	if body.Username != "root" || body.Password != "123" {
		ctx.JSON(http.StatusBadRequest, gin.H{"status": "304"})
		return
	}
	ctx.JSON(http.StatusOK, gin.H{"data": body.Hobby})
}

// 绑定参数 - 绑定url
// /bind/url/donghuan/1
func BindUrl(ctx *gin.Context) {
	var person Person
	if err := ctx.ShouldBindUri(&person); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	ctx.JSON(200, gin.H{"name": person.Name, "uuid": person.Id})
}

// 自定义验证器
func DiyValidtor(ctx *gin.Context) {
	var b Booking
	if err := ctx.ShouldBindWith(&b, binding.Query); err == nil {
		ctx.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
	} else {
		ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
	}
}

// orm快速入门
func OrmTest1(ctx *gin.Context) {
	// 创建
	userInfo := models.UserInfo{1, "donghuan", "男", "篮球"}
	models.DB.Debug().Create(&userInfo)
	// 查询
	var u = new(models.UserInfo)
	//models.DB.Debug().First(&u)
	models.DB.Debug().First(&u, 1)
	models.DB.Debug().First(&u, "name=?", "donghuan")
	// SELECT * FROM `user_infos` WHERE name='donghuan' AND `user_infos`.`id` = 1 ORDER BY `user_infos`.`id` LIMIT 1
	fmt.Printf("first %#v\n", u)

	var uu = new(models.UserInfo)
	models.DB.Debug().Find(&uu, "name=?", "donghuan")
	//SELECT * FROM `user_infos` WHERE name='donghuan'
	fmt.Printf("find %#v\n", uu)

	// 更新
	models.DB.Debug().Model(&uu).Update("name", "donghuan1")
	// UPDATE `user_infos` SET `name`='donghuan1' WHERE `id` = 1

	models.DB.Debug().Find(&uu, "name=?", "donghuan1")
	fmt.Printf("find %#v\n", uu)

	// 删除
	models.DB.Debug().Delete(&uu)
	// DELETE FROM `user_infos` WHERE `user_infos`.`id` = 1
	ctx.JSON(200, gin.H{
		"msg": "ok",
	})
}

// 结构体标签
func OrmTest2(ctx *gin.Context) {

}

// crud
func OrmTest3(ctx *gin.Context) {
	// 1.创建(不存在就创建)， 默认值及指针字段(memberNumber)
	// 所有字段的零值, 比如0, "",false或者其它零值，都不会保存到数据库内，但会使用他们的默认值
	//member := new(string)
	//*member = "111"
	//user := models.UserInfo2{Name: "zhu_new", Age: sql.NullInt64{18, true}, Birthday: time.Now(), MemberNumber: new(string)}
	//user2 := models.UserInfo2{Name: "zhu111", Age: sql.NullInt64{0, true}, Birthday: time.Now(), MemberNumber: member}
	////SELECT * FROM `user_info2` WHERE `user_info2`.`deleted_at` IS NULL ORDER BY `user_info2`.`id` LIMIT 1
	////INSERT INTO `user_info2` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`,`email`,`role`,`member_number`,`num`,`address`) VALUES ('2021-05-09 19:07:24.172','2021-05-09 19:07:24.172',NULL,'zhu',18,'2021-05-09 19:07:24.168','','','小丸子',0,'')

	//models.DB.Debug().FirstOrCreate(&user)
	//models.DB.Debug().Create(&user2)
	// 扩展选项，还没有发现有什么作用
	//models.DB.Debug().Set("gorm:insert_option", "ON CONFLICT").Create(&user2)

	// 2.一般查询
	//user := models.UserInfo2{}

	//models.DB.First(&user)
	//models.DB.Last(&user)
	//models.DB.Take(&user)
	//models.DB.Find(&user)
	//models.DB.Find(&user, 5)
	//fmt.Printf("user: %#v\n", &user)

	// 3.where查询
	// 3.1 普通查询
	//user := models.UserInfo2{}
	// 找到第一个
	//models.DB.Debug().Where("name=?", "zhu111").First(&user)
	//fmt.Printf("user: %#v\n", &user)

	// 拿到所有的  todo
	//users := []models.UserInfo2{}
	//models.DB.Debug().Where("name = ?", "zhu").Find(&users)
	//models.DB.Debug().Where("name <> ?", "zhu111").Find(&users)
	//models.DB.Debug().Where("name in (?)", []string{"zhu111", "zhu"}).Find(&users)
	//models.DB.Debug().Where("name like ?", "%zhu%").Find(&users)
	//models.DB.Debug().Where("name = ? and age = ?", "zhu", 18).Find(&users)
	//lastTime, _ := time.ParseInLocation("2006-01-02 15:04:05", "2021-05-09 22:38:32", time.Local)
	//models.DB.Debug().Where("updated_at < ? ", lastTime).Find(&users)
	//models.DB.Debug().Where("created_at BETWEEN ? AND ? ", startTime, endTime).Find(&users)
	//fmt.Printf("user: %#v\n", &users)

	//3.2 struct & map 查询
	//user := models.UserInfo2{}
	//users := []models.UserInfo2{}
	//models.DB.Where(&models.UserInfo2{Name: "zhu"}).Find(&user)
	//models.DB.Where(map[string]interface{}{"name": "zhu"}).Find(&user)
	// 主键切片
	//models.DB.Where([]int64{4, 5}).Find(&users)
	// 注意：通过结构体查询时，对于零值字段，会被忽略, 下面的查询不到结果，结构体定义的原因
	//models.DB.Debug().Where(&models.UserInfo2{Name: "zhu", Age: 0}}).Find(&users)
	//// SELECT * FROM users WHERE name = "zhu";
	//fmt.Printf("user: %#v\n", &users)

	//4，not
	//user := models.UserInfo2{}
	//users := []models.UserInfo2{}
	//models.DB.Debug().Not("name", "zhu").First(&user)
	// not in
	//models.DB.Debug().Not("name", []string{"zhu111"}).Find(&users)
	// not in base on primary key
	//models.DB.Debug().Not([]int64{1}).Find(&users)
	//models.DB.Debug().Not([]int64{}).Find(&users)

	// plain sql
	//models.DB.Debug().Not("name = ?", "zhu").Find(&users)

	// not in struct
	//models.DB.Debug().Not(models.UserInfo2{Name: "zhu111"}).Find(&users)
	//fmt.Printf("user: %#v\n", &users)

	//5. or 通常会和 where结合使用
	//users := []models.UserInfo2{}
	//models.DB.Debug().Where("name=?", "zhu").Or("age=?", 19).Find(&users)
	// struct
	//models.DB.Debug().Where("name=?", "zhu").Or(models.UserInfo2{Age: sql.NullInt64{19, true}}).Find(&users)
	// map
	//models.DB.Debug().Where("name=?", "zhu").Or(map[string]interface{}{"age": 19}).Find(&users)
	//fmt.Printf("user: %#v\n", &users)

	//6. 内联条件 find,first, 不通过where
	//user := models.UserInfo2{}
	// id
	//models.DB.Debug().First(&user, 1)
	//models.DB.Debug().First(&user, "id=?" ,"string_primary_key")
	//models.DB.Debug().Find(&user, "name=?","zhu")
	//models.DB.Debug().Find(&user, "name=? and age=?","zhu111", 18 )
	// struct
	//models.DB.Debug().Find(&user, models.UserInfo2{Name: "zhu"})
	// map
	//models.DB.Debug().Find(&user, map[string]interface{}{"name":"zhu"})
	//fmt.Printf("user: %#v\n", &user)

	// 7.额外查询选项
	//user := models.UserInfo2{}
	/*
		这个功能是上锁。上的是一个排它锁,也就是说,其他的事务是可以读取的。但是不能写入或者更新。
	*/
	//models.DB.Debug().Set("gorm:query_option", "FOR UPDATE").Find(&user, 3)

	//8. FirstOrInit  获取匹配的第一条记录，否则根据给定的条件初始化一个新的对象 (仅支持 struct 和 map 条件)， 不会更改数据库的记录
	// 数据库中没有记录，仅仅是初始化了一个对象
	//user := models.UserInfo2{}
	// 未找到, 数据库中没有记录，仅仅是初始化了一个对象
	//models.DB.Debug().FirstOrInit(&user, models.UserInfo2{Name: "not_existing"})
	//fmt.Printf("user: %#v\n", &user.Name)
	//fmt.Println(user.Name)

	// 找到,直接查找出来
	//models.DB.Debug().Where("name=?", "zhu").FirstOrInit(&user)
	//models.DB.Debug().FirstOrInit(&user, map[string]interface{}{"name": "zhu"})
	//fmt.Printf("user: %#v\n", &user)
	//fmt.Println(user.Name)

	// attrs 如果记录未找到，将使用参数初始化 struct.
	// 未找到
	//models.DB.Debug().Where("name", "not_exiting").Attrs(models.UserInfo2{Age: sql.NullInt64{18, true}}).FirstOrInit(&user)
	//models.DB.Debug().Where("name", "not_exiting").Attrs("age", 20).FirstOrInit(&user)
	//fmt.Printf("user: %#v\n", &user)
	//fmt.Println(user.Name)
	//fmt.Println(user.Age)
	//
	//// 找到， 直接查找数据库中的记录，attrs无效
	//models.DB.Debug().Where("name", "zhu").Attrs("age", 20).FirstOrInit(&user)
	//fmt.Printf("user: %#v\n", &user)
	//fmt.Println(user.Name)
	//fmt.Println(user.Age)

	// asign 不管记录是否找到，都将参数赋值给 struct. 但是不会直接更改数据库的记录
	// 未找到
	//models.DB.Debug().Where("name","zhu1111").Assign("age", 20).FirstOrInit(&user)
	//fmt.Printf("user: %#v\n", &user)
	//fmt.Println(user.Name)
	//fmt.Println(user.Age)
	//
	//// 找到
	//models.DB.Debug().Where("name","zhu").Assign("age", 20).FirstOrInit(&user)
	//fmt.Printf("user: %#v\n", &user)
	//fmt.Println(user.Name)
	//fmt.Println(user.Age)

	//9. FirstOrCreate  获取匹配的第一条记录, 否则根据给定的条件创建一个新的记录 (仅支持 struct 和 map 条件)
	//user := models.UserInfo2{}
	//models.DB.Debug().FirstOrCreate(&user, models.UserInfo2{Name: "zhu222"})
	//fmt.Printf("user: %#v\n", &user)
	//fmt.Println(user.Name)
	//fmt.Println(user.Age)

	// attrs
	// assign

	//10.高级查询
	//10.1 子查询
	//user := []models.UserInfo2{}
	//models.DB.Debug().Where("amount > (?)", models.DB.Table("orders").Select("AVG(amount)").Where("state = ?", "paid")).Find(&user)
	// SELECT * FROM "orders"  WHERE "orders"."deleted_at" IS NULL AND (amount > (SELECT AVG(amount) FROM "orders"  WHERE (state = 'paid')));
	// SELECT * FROM `user_info2` WHERE amount > (SELECT AVG(amount) FROM `orders` WHERE state = 'paid') AND `user_info`.`deleted_at` IS NULL

	//10.2 选择字段  Select，指定你想从数据库中检索出的字段，默认会选择全部字段。
	//models.DB.Debug().Select("name", "age").Where("name","zhu").Find(&user)
	//fmt.Printf("user: %#v\n", &user)
	//fmt.Println(user.Name)
	//fmt.Println(user.Age)

	//models.DB.Debug().Table("user_info2").Select("age").Last(&user,"age>?", 18)
	//fmt.Printf("user: %#v\n", &user)
	//fmt.Println(user.Name)
	//fmt.Println(user.Age)

	//10.3 order
	//models.DB.Debug().Order("age desc, name").Last(&user)
	//fmt.Printf("user: %#v\n", &user)
	//fmt.Println(user.Name)
	//fmt.Println(user.Age)

	// 覆盖排序, v2已经没有改方式
	//models.DB.Debug().Order("age desc").First(&user).Order("age", true).First(&user)
	//models.DB.Debug().Clauses(clause.OrderBy{
	//	Expression: clause.Expr{SQL: "FIELD (id,?)",
	//	Vars: []interface{}{[]int{1, 2, 3}},
	//	WithoutParentheses: true}}).Find(&user)
	//fmt.Printf("user: %#v\n", &user)

	//10.4 limit
	//users := []models.UserInfo2{}
	//models.DB.Debug().Limit(3).Find(&users)
	//fmt.Printf("user: %#v\n", &users)

	//10.5 offset
	//users := []models.UserInfo2{}
	// models.DB.Debug().Offset(3).Find(&users) 会报错
	//models.DB.Debug().Limit(1).Offset(3).Find(&users)
	//models.DB.Debug().Offset(3).Limit(1).Find(&users)
	//fmt.Printf("user: %#v\n", &users)

	//10.6 count
	//Count 必须是链式查询的最后一个操作 ，因为它会覆盖前面的 SELECT，但如果里面使用了 count 时不会覆盖
	//count := new(int64)
	//models.DB.Debug().Where("name", "zhu").Or("age", 18).Find(&users).Count(count)
	//models.DB.Debug().Model(models.UserInfo2{}).Where("name","zhu111").Count(count)
	//models.DB.Debug().Table("user_info2").Count(count)
	//models.DB.Debug().Table("user_info2").Select("count(distinct(name))").Count(count)
	//fmt.Printf("user: %#v\n", &users)
	//fmt.Println("count:", *count)

	//10.7 group & having
	//count := new(int64)
	//var rets []models.Result
	//models.DB.Debug().Table("user_info2").Select("date(created_at) as date, sum(age) as total").Group("date(created_at)").Scan(&rets)
	// SELECT date(created_at) as date, sum(age) as total FROM `user_info2` GROUP BY date(created_at)

	//rows, _ := models.DB.Debug().Table("user_info2").Select("date(created_at) as date, sum(age) as total").Group("date(created_at)").Having("sum(age) > ?", 30).Rows()
	//for rows.Next() {
	//	// todo   https://www.cnblogs.com/jiujuan/p/12676195.html
	//}

	//models.DB.Debug().Table("user_info2").Select("date(created_at) as date, sum(age) as total").Group("date(created_at)").Having("sum(age) > ?", 30).Scan(&rets)

	//fmt.Printf("result: %#v\n", &rets)

	//10.8 join
	//var rets []models.Result
	//user := models.UserInfo2{}
	//
	//models.DB.Debug().Table("user_info2").Select("user.name", "email.name").Joins("left join email on email.id = user.id").Scan(&rets)

	// 多连接及参数

	// SELECT `user_info2`.`id`,`user_info2`.`created_at`,`user_info2`.`updated_at`,`user_info2`.`deleted_at`,`user_info
	//2`.`name`,`user_info2`.`age`,`user_info2`.`birthday`,`user_info2`.`email`,`user_info2`.`role`,`user_info2`.`member_number`,`user_info2`.`num`,`user_info
	//2`.`address` FROM `user_info2` JOIN emails ON emails.user_id = users.id AND emails.email = 'jinzhu@example.org' JOIN credit_cards ON credit_cards.user_i
	//d = users.id WHERE credit_cards.number = '411111111111' AND `user_info2`.`deleted_at` IS NULL
	//
	//models.DB.Debug().Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)

	//11。pluck  Pluck，查询 model 中的一个列作为切片, todo, 有bug
	//var num []int
	//models.DB.Debug().Table("user_info2").Pluck("num", &num)
	//models.DB.Debug().Model(&users).Pluck("num", &num)
	//models.DB.Debug().Table("user_info2").Pluck("num", &num)
	//fmt.Printf("result: %#v\n", &num)

	//12. 扫描    如果您想要查询多个列，您应该使用 Scan, 扫描结果至一个 struct.
	var result models.Result1
	var results []models.Result1
	models.DB.Debug().Table("user_info2").Where("name", "zhu").Scan(&result)
	models.DB.Debug().Table("user_info2").Where("id>?", 0).Scan(&results)
	fmt.Printf("result: %#v\n", &result)
	fmt.Printf("result1: %#v\n", &results)

	ctx.JSON(200, gin.H{
		"msg": "ok",
	})
}

// 链式调用
func OrmChain(ctx *gin.Context) {
	//链式操作
	info2 := models.UserInfo2{}
	query := models.DB.Debug().Where("name", "zhu")
	if 1 > 0 {
		query.Where("age", 18)
	} else {
		query.Where("age", 19)
	}

	query.Find(&info2)

	//	 常见的立即执行方法
	/*
		find, take , last, first
		create
		update
		delete
		row
		rows
		scan
	*/

	// 范围  Scope是建立在链式操作的基础之上的
	// 基于它，你可以抽取一些通用逻辑，写出更多可重用的函数库。
	users := []models.UserInfo2{}
	models.DB.Debug().Scopes(AmountGreaterThan1000, PaidWithCreditCard).Find(&users)
	models.DB.Debug().Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&users)

}

func OrmUpdate(ctx *gin.Context) {
	//1. save  默认会更新该对象的所有字段，即使你没有赋值。
	user := models.UserInfo2{}
	models.DB.First(&user)
	//user.Name = "jinzhu"
	// UPDATE `user_info2` SET `created_at`='2021-05-09 22:36:32.794',`updated_at`='2021-05-11 22:51:49.078',`deleted_at`=NULL,`name`='jinzhu',`age`=18,`birthday`='2021-05-09 22:36:32.727',`email`='',`role`='',`member_number`='',`num`=0,`address`='' WHERE `id` = 4
	//models.DB.Debug().Save(&user)

	//2. 更新部分字段  Update或者Updates
	// 更新单个字段
	// UPDATE `user_info2` SET `name`='zhu',`updated_at`='2021-05-11 22:56:49.393'
	//models.DB.Debug().Model(&user).Update("name", "zhu")

	// 根据给定的条件更新单个字段
	// UPDATE `user_info2` SET `name`='zhu',`updated_at`='2021-05-11 22:58:50.407' WHERE `age` = 19
	//models.DB.Debug().Model(&user).Where("age", 19).Update("name", "zhu")

	// map, 只会更新有变化的值
	// UPDATE `user_info2` SET `age`=17,`name`='zhu',`updated_at`='2021-05-12 00:08:44.007' WHERE `id` = 4
	//models.DB.Debug().Model(&user).Updates(map[string]interface{}{"name":"zhu", "age":17})

	// struct, 只会更新有变化且是非零的字段
	//UPDATE `user_info2` SET `updated_at`='2021-05-12 00:10:21.226',`name`='zhu111' WHERE `id` = 4
	//models.DB.Debug().Model(&user).Updates(models.UserInfo2{Name: "zhu111", Age: sql.NullInt64{0, true}})

	//3. 更新选择字段
	//UPDATE `user_info2` SET `updated_at`='2021-05-12 00:13:47.83',`name`='zhu222' WHERE `id` = 4
	//models.DB.Debug().Model(&user).Select("name").Updates(models.UserInfo2{Name: "zhu222", Age: sql.NullInt64{10, true}})
	// UPDATE `user_info2` SET `updated_at`='2021-05-12 00:14:33.782',`age`=11 WHERE `id` = 4
	//models.DB.Debug().Model(&user).Omit("name").Updates(models.UserInfo2{Name: "zhu333", Age: sql.NullInt64{11, true}})

	//4. 无hooks更新  UpdateColumn， UpdateColumns, 不设置updated_at
	// UPDATE `user_info2` SET `name`='zhu' WHERE `id` = 4
	//models.DB.Debug().Model(&user).UpdateColumn("name", "zhu")
	// UPDATE `user_info2` SET `name`='zhu11',`age`=12 WHERE `id` = 4
	//models.DB.Debug().Model(&user).UpdateColumns(models.UserInfo2{Name: "zhu11", Age: sql.NullInt64{12, true}})

	//5.批量更新  批量更新时，hooks不会运行， 主要区别在于model的不同，不是具体的对象
	// error, 无效果
	//models.DB.Debug().Model(models.UserInfo2{}).Update("num", 1)
	// struct
	// 使用 struct 更新时，只会更新非零值字段，若想更新所有字段，请使用map[string]interface{}
	//models.DB.Debug().Model(models.UserInfo2{}).Where("name", "zhu").Updates(models.UserInfo2{Name: "zhu_new"})
	// map
	//models.DB.Debug().Model(models.UserInfo2{}).Where("name", "zhu_new").Updates(map[string]interface{}{"name": "zhu_111"})

	//6.使用sql表达式更新
	// UPDATE `user_info2` SET `num`=num * 2 + 1,`updated_at`='2021-05-12 00:28:44.747' WHERE `id` = 4
	//models.DB.Debug().Model(&user).Update("num", gorm.Expr("num * ? + ?", 2, 1))
	//UPDATE `user_info2` SET `num`=num * 2 + 1,`updated_at`='2021-05-12 00:30:34.177' WHERE `id` = 4
	//models.DB.Debug().Model(&user).Updates(map[string]interface{}{"num": gorm.Expr("num * ? + ?", 2, 1)})

	//UPDATE `user_info2` SET `age`=num * 2 + 1 WHERE `id` = 4
	//models.DB.Debug().Model(&user).UpdateColumn("age", gorm.Expr("num * ? + ?", 2, 1))

	//UPDATE `user_info2` SET `age`=age - 1 WHERE age > 10 AND `id` = 4
	//models.DB.Debug().Model(&user).Where("age > 10").UpdateColumn("age", gorm.Expr("age - ?", 1))

	//7. 修改Hooks中的值  如果你想修改 BeforeUpdate, BeforeSave 等 Hooks 中更新的值，你可以使用 scope.SetColumn, 例如
	// users.go 中定义了修改hooks的方法
	// 确保数据库中 r.name = "zhu"
	//models.DB.Debug().Model(&user).Update("name", "zhu11")

	//8. 其它更新选项
	//// 为 update SQL 添加其它的 SQL
	// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111 OPTION (OPTIMIZE FOR UNKNOWN);
	models.DB.Debug().Model(&user).Set("gorm:update_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Update("name", "hello")

}

func OrmDelete(ctx *gin.Context) {
	info := []models.UserInfo2{}
	//models.DB.Debug().Find(&info, "name", "zhu")
	//查找的时候去除 delete_at
	//方式一
	models.DB.Debug().Where("name = ?", "zhu222").Where("deleted_at is not null").Unscoped().First(&info)
	//方式二 原生sql

	// 软删除
	// UPDATE `user_info2` SET `deleted_at`='2021-05-12 10:21:14.992' WHERE `user_info2`.`id` = 4 AND `user_info2`.`deleted_at` IS NULL
	//models.DB.Debug().Delete(&info)
	// 为sql添加额外的sql操作
	//models.DB.Debug().Set("gorm:delete_option","OPTION (OPTIMIZE FOR UNKNOWN)").Delete(&info)

	//批量删除&软删除
	//models.DB.Debug().Where("name like ?", "%jinzhu%").Delete(models.UserInfo2{})
	//models.DB.Debug().Delete(models.UserInfo2{}, "name like ?", "%jinzhu%")
	//models.DB.Debug().Where("age>=?", 18).Delete(&info)
	// 查询记录时会忽略被软删除的记录
	//// SELECT * FROM users WHERE age >= 18 AND deleted_at IS NULL;
	//models.DB.Debug().Where("age >= 18").Find(&info)

	//硬删除
	// todo 如何在查找的时候去除 delete_at
	// DELETE FROM `user_info2` WHERE `user_info2`.`id` = 1
	models.DB.Debug().Unscoped().Delete(&info)

	ctx.JSON(200, gin.H{
		"msg": "ok",
	})
}

func TestString(ctx *gin.Context) {
	//var echo = fmt.Println
	//s := "aabcd12345"
	//contains := strings.Contains(s, "123456")
	//fmt.Println("contains:", contains)

	//count := strings.Count(s, "a")
	//fmt.Println("count:", count)

	//prefix := strings.HasPrefix(s, "aab")
	//fmt.Println("prefix:", prefix)

	//echo("suxprefix:", strings.HasSuffix(s, "45"))
	//echo("index:", strings.Index(s, "45"))
	//echo("join:", strings.Join([]string{s, "456"}, ","))  //aabcd12345,456
	//echo("repeat:", strings.Repeat(s, 2))
	//echo("replace:", strings.Replace(s,"a", "A", -1))
	//echo("replace:", strings.Replace(s,"a", "A", 1))
	//echo("split:", strings.Split(s, "a"))              // [  bcd12345]
	//echo("split:", strings.Split("a-b-c-d-e", "-")) // [a b c d e]
	//echo("tolowwer:", strings.ToLower(s))
	//echo("toUpper:", strings.ToUpper(s))
	//echo("len:", len(s))
	//echo("char:", s[1])

	//echo("trim:", strings.Trim(" ====hell o===  ", ""))
	//echo("trim:", strings.Trim(" ====hell o===  ", " "))
	//echo("trim:", strings.Trim(" ====hell o===  ", "="))  // trim:  ====hell o=== 因为首位都不包含=
	//echo("trim:", strings.Trim("====hell o===   ", "= ")) // trim: hell o
	//echo("trim:", strings.Trim(" ====hell o===  ", " =")) // trim: hell o
	//echo("trim:", strings.Trim(" ====hell o=i==  ", " =")) // trim: hell o=i

	//echo("fields:", strings.Fields(" ====hell o===  "))

	//rot13 := func(r rune) rune { // r是遍历的每一个字符
	//	switch {
	//	case r >= 'A' && r <= 'Z':
	//		return 'A' + (r-'A'+13)%26
	//	case r >= 'a' && r <= 'z':
	//		return 'a' + (r-'a'+13)%26
	//	}
	//	return r
	//}
	//fmt.Println(strings.Map(rot13, "'Twas brillig and the slithy gopher..."))

	//*******************************类型转换*************	****************
	// 将其他类型转换成string
	//formatBool := strconv.FormatBool(true)
	//fmt.Printf("formatbool: %T\n", formatBool)
	//fmt.Println("formatbool: ", formatBool)

	// 将字符串转成 xxx
	//parseBool, _ := strconv.ParseBool("true")
	//fmt.Printf("parseBool: %T\n", parseBool)
	//fmt.Println("parseBool: ", parseBool)

	// 将其他类型转成字符串后, 添加到现有的字节数组中
	// https://blog.csdn.net/houyanhua1/article/details/88659791
	//b := make([]byte, 0)
	//resultBool := strconv.AppendBool(b, true)
	//fmt.Println(resultBool)
	//fmt.Println(string(resultBool))

	//slice2 := strconv.AppendInt(b,123,2) // 2:二进制
	//fmt.Println(string(slice2))                 // 1111011

	//slice3 := strconv.AppendFloat(b, 3.141592, 'f', 4, 64) // 4:保留4位。 64:float64
	//fmt.Println(string(slice3))                                               // 3.1416

	//slice4 := strconv.AppendQuote(b,"hello")
	//fmt.Println(string(slice4))  // "hello" (包含双引号)

	ctx.JSON(200, gin.H{
		"msg":  "ok",
		"code": 200,
	})
}

func TestFile(ctx *gin.Context) {
	fp, err := os.Create("./file.txt")
	if err != nil {
		ctx.JSON(200, gin.H{
			"msg": "error",
		})
	}
	defer fp.Close()

	/*
	   os.Create()
	   os.Open()
	   os.OpenFile()

	   fp.Write()
	   fp.WriteString()
	   fp.Read()
	   fp.Seek(offset, 0/1/2)
	   fp.WriteAt([]bytes, offset)

	   bufio.NewReader().ReadBytes() 等价于 b := make([]byte, 1024)  fp.Read(b)

	*/

	ctx.JSON(200, gin.H{
		"msg": "ok",
	})
}

func TestJson(ctx *gin.Context) {

	// json到结构体
	//type Server struct {
	//	ServerName string
	//	ServerIP   string
	//}
	//type Serverslice struct {
	//	Servers []Server
	//}
	//
	//var s Serverslice
	//str := `{"servers":
	//[{"serverName":"Guangzhou_Base","serverIP":"127.0.0.1"},
	//{"serverName":"Beijing_Base","serverIP":"127.0.0.2"}]}`
	//err := json.Unmarshal([]byte(str), &s)
	//if err != nil {
	//	fmt.Println(err)
	//}
	//fmt.Printf("s: %#v\n", s)
	//fmt.Println(s.Servers[0].ServerName)

	// json到map
	b := []byte(`{"IP": "127.0.0.1", "name": "SKY"}`)

	m := make(map[string]string)

	err := json.Unmarshal(b, &m)
	if err != nil {
		fmt.Println("Umarshal failed:", err)
		return
	}

	fmt.Println("result:", m)

	for k, v := range m {
		fmt.Println(k, ":", v)
	}

	ctx.JSON(200, gin.H{
		"msg": "ok",
	})
}

func TestTime(ctx *gin.Context) {
	// 时间是固定的 2006-01-02 15:04:05
	//layout_time := "2006-01-02 15:04:05"
	//use_time := "2021-05-18 01:02:03"
	//// parse  将时间字符串转成 time.Time
	//time_date, _ := time.Parse(layout_time, use_time)
	//fmt.Printf("type: %#v\n", time_date)
	//fmt.Println(time_date)
	//fmt.Println(time_date.Unix())
	//fmt.Println(time.Now().Unix())
	//fmt.Println(time.Now().Year())
	//fmt.Println(time.Now().Location())
	//fmt.Println(time.Now().Local())

	// format   将time.Time 转成 string
	//timestamp := time.Now().Unix()
	//result := time.Unix(timestamp, 0).Format("2006-01-02 15:04:05")
	//fmt.Println(result)
	//fmt.Println(reflect.TypeOf(result))

	// 有很多的格式化类型
	// 2006-01-02 15:04:05 -0700

	//根据指定时间返回time.Time
	//分别指定年，月，日，时，分，秒，纳秒，时区
	t3 := time.Date(2017, time.Month(5), 26, 15, 30, 20, 0, time.Now().Location())
	fmt.Println(t3)
	fmt.Println(reflect.TypeOf(t3))

	// 进阶
	//https://www.cnblogs.com/angelyan/p/11104610.html
	//https://zhuanlan.zhihu.com/p/343014745
	//https://blog.csdn.net/wschq/article/details/80114036

	ctx.JSON(200, gin.H{
		"msg": "ok",
	})
}

func TestRe(ctx *gin.Context) {
	/*
		compile
		MustCompile
		它们的区别是Compile返回两个参数*Regexp,error类型，而MustCompile只返回*Regexp类型
			func Compile(expr string) (*Regexp, error) {}
			func MustCompile(str string) *Regexp {}
		它们的作用是将正则表达式进行编译，返回优化的 Regexp 结构体

		match
		matchstring

		replace
		findxxx
		split
	*/
	//// 是否匹配
	//fmt.Println(regexp.Match("H.* ", []byte("Hello World!")))
	//sourceStr := `my email is gerrylon@163.com`
	//matched, _ := regexp.MatchString(`[\w]+@[\w]+(?:\.[\w]+)+`, sourceStr)
	//fmt.Printf("%v\n", matched) // true

	//提取子分组
	//var sourceStr string = `
	//test text lljflsdfjdskal
	//gerrylon@163.com
	//abc@gmail.com
	//someone@sina.com.cn`
	//
	//re := regexp.MustCompile(`[\w-]+@([\w]+(?:\.[\w]+)+)`)
	//matched := re.FindAllStringSubmatch(sourceStr, -1)
	//for _, match := range matched {
	//	fmt.Printf("email is: %s, domain is: %s\n", match[0], match[1])
	//}

	//输出匹配串的起始结束索引
	//var sourceStr string = `gerrylon@163.com, abc@gmail.com, someone@sina.com.cn`
	//re := regexp.MustCompile(`[\w-]+@([\w]+(?:\.[\w]+)+)`)
	//matched := re.FindAllStringIndex(sourceStr, -1)
	//for _, pos := range matched {
	//	fmt.Printf("start is: %v, end is: %v\n", pos[0], pos[1])
	//}

	/*
		总结：
			Find(All)?(String)?(Submatch)?(Index)?
			如果带有All，就找到所有的（和相关参数配合使用）
			如果带有String，参数就是string, 否则就是slice of byte
			如果带有Submatch，就能匹配分组
			如果带有Index， 就以索引返回
	*/

	ctx.JSON(200, gin.H{
		"msg": "ok",
	})
}

func TestReflect(ctx *gin.Context) {
	// https://studygolang.com/articles/12348?fr=sidebar
	// https://www.cnblogs.com/gauze/p/8984581.html
	// https://www.cnblogs.com/coder2012/p/4881854.html
	// https://blog.csdn.net/xhd731568849/article/details/79198048
	// http://c.biancheng.net/golang/reflect/

	// 因为Golang本身不支持模板，因此在以往需要使用模板的场景下往往就需要使用反射(reflect)来实现
	/*
		 Golang reflect慢主要有两个原因:
			涉及到内存分配以及后续的GC
			reflect实现里面有大量的枚举，也就是for循环，比如类型之类的
	*/
	//1.
	var num float64 = 1.2345
	fmt.Println("type: ", reflect.TypeOf(num))

	//2. 当执行reflect.ValueOf(interface)之后，就得到了一个类型为”relfect.Value”变量
	/*
		说明:
			转换的时候，如果转换的类型不完全符合，则直接panic，类型要求非常严格！
			转换的时候，要区分是指针还是指
			也就是说反射可以将“反射类型对象”再重新转换为“接口类型变量”
	*/
	// 已知原有类型【进行“强制转换”】
	//pointer := reflect.ValueOf(&num)
	//value := reflect.ValueOf(num)
	//convertPointer := pointer.Interface().(*float64)
	//convertValue := value.Interface().(float64)
	//fmt.Println(convertPointer)
	//fmt.Println(convertValue)

	// 未知原有类型【遍历探测其Filed】
	//user := User1{1, "Allen.Wu", 25}
	//DoFiledAndMethod(user)

	//3. 通过reflect.Value设置实际变量的值
	// 注意一定要是指针
	//pointer := reflect.ValueOf(&num)
	//newValue := pointer.Elem()
	//
	//fmt.Println("type of pointer:", newValue.Type())
	//fmt.Println("settability of pointer:", newValue.CanSet())
	//
	//// 重新赋值
	//newValue.SetFloat(77)
	//fmt.Println("new value of pointer:", num)

	//4.通过reflect.ValueOf来进行方法的调用
	user := User1{1, "Allen.Wu", 25}
	getValue := reflect.ValueOf(&user)
	// 先看看带有参数的调用方法
	methodValue := getValue.MethodByName("ReflectCallFuncHasArgs")
	args := []reflect.Value{reflect.ValueOf("wudebao"), reflect.ValueOf(30)}
	methodValue.Call(args)

	// 再看看无参数的调用方法
	methodValue = getValue.MethodByName("ReflectCallFuncNoArgs")
	args = make([]reflect.Value, 0)
	methodValue.Call(args)

	ctx.JSON(200, gin.H{
		"msg": "ok",
	})
}

func TestNet(ctx *gin.Context) {
	requestUrl := "http://www.baidu.com"
	//1. get
	//response, err := http.Get(url)
	//
	//if err != nil {
	//	fmt.Println(err)
	//}
	//
	//defer response.Body.Close()
	//result, _ := ioutil.ReadAll(response.Body)
	//fmt.Println(string(result))

	//2. post
	//postValue := url.Values{
	//	"username": {"hangmeimei"},
	//	"address":  {"anhui"},
	//	"subject":  {"world"},
	//	"form":     {"beij"},
	//}
	// postform
	//response, err := http.PostForm(requestUrl, postValue)

	// post
	//body := bytes.NewBufferString(postValue.Encode())
	//response, err := http.Post(requestUrl, "text/html", body)
	//if err != nil {
	//	fmt.Println(err)
	//}
	//defer response.Body.Close()
	//if response.StatusCode == 200 {
	//	rb, _ := ioutil.ReadAll(response.Body)
	//	fmt.Println(string(rb))
	//}

	//3. do
	client := &http.Client{}
	//request, err := client.Get(requestUrl)
	request, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		fmt.Println(err)
	}
	cookie := &http.Cookie{Name: "Tom", Value: strconv.Itoa(123)}
	request.AddCookie(cookie)
	request.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml")
	request.Header.Set("Accept-Charset", "utf-8")
	request.Header.Set("Accept-Encoding", "gzip")
	request.Header.Set("Accept-Language", "zh-CN,zh")
	request.Header.Set("Cache-Control", "max-age=0")
	request.Header.Set("Connection", "keep-alive")

	response, err := client.Do(request)
	if err != nil {
		fmt.Println(err)
		return
	}

	defer response.Body.Close()
	fmt.Println(response.StatusCode)
	if response.StatusCode == 200 {
		r, err := ioutil.ReadAll(response.Body)
		if err != nil {
			fmt.Println(err)
		}
		fmt.Println(string(r))
	}

	/*
		server端代码：

		func SayHello(w http.ResponseWriter, req *http.Request) {
		    w.Write([]byte("Hello"))
		}

		func main() {
		    http.HandleFunc("/hello", SayHello)
			//http.Handle("/", xxx)
		    http.ListenAndServe(":8080", nil)
		}

	*/

	//4. 简单的源码分析
	//https://www.dazhuanlan.com/2020/03/27/5e7d5251d474e/

	//5. 参考资料
	/*
		https://studygolang.com/pkgdoc
		https://www.php.cn/manual/view/35252.html
	*/

	ctx.JSON(200, gin.H{
		"msg":  "ok",
		"code": 200,
	})
}
